#ifndef     __ENCODE_H
#define     __ENCODE_H

#include "./base.h"

namespace serialize {

inline void encode_uint(uint8_t *buf, uint8_t value) {
    buf[0] = value;
}

inline void encode_uint(uint8_t *buf, uint16_t value) {
    buf[0] = value >> 8;
    buf[1] = value;
}

inline void encode_uint(uint8_t *buf, uint32_t value) {
    buf[0] = value >> 24;
    buf[1] = value >> 16;
    buf[2] = value >> 8;
    buf[3] = value;
}

inline constexpr usize_t encode(uint8_t *buf) {
    return 0;
}

template <typename T, typename... Ts>
inline usize_t encode(uint8_t *buf, T&& value, Ts&&... ts) {
    using TYPE = std::decay_t<T>;

    if constexpr ( std::is_unsigned_v<TYPE> ) {
        encode_uint(buf, value);
        constexpr usize_t len = sizeof (TYPE);
        if constexpr (sizeof...(Ts) != 0) {
            return len + encode(buf + len, std::forward<Ts>(ts)...);
        } else {
            return len;
        }
    } else if constexpr (std::is_signed_v<TYPE>) {
        using UINT = std::make_unsigned_t<TYPE>;
        return encode(buf, static_cast<UINT>(value), std::forward<Ts>(ts)...);
    } else if constexpr ( std::is_enum_v<TYPE> ) {
        using UINT = std::make_unsigned_t<TYPE>;
        return encode(buf, static_cast<UINT>(value), std::forward<Ts>(ts)...);
    } else if constexpr ( std::is_class_v<TYPE> ) {
        usize_t len = value.encode(buf);
        if constexpr (sizeof...(Ts) != 0) {
            return len + encode(buf + len, std::forward<Ts>(ts)...);
        } else {
            return len;
        }
    }
}

}

#endif
